package com.itcp.itelpicture.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.itcp.itelpicture.constants.RedisConstants;
import com.itcp.itelpicture.domain.dto.remark.LikeRecordFormDTO;
import com.itcp.itelpicture.domain.dto.remark.LikedTimesDTO;
import com.itcp.itelpicture.domain.po.LikeRecord;
import com.itcp.itelpicture.domain.po.User;
import com.itcp.itelpicture.domain.vo.UserVO;
import com.itcp.itelpicture.mapper.LikeRecordMapper;
import com.itcp.itelpicture.service.ILikeRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itcp.itelpicture.service.ILikedStatService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 *
 * @author wgzz
 * @since 2025-04-20
 */
@Service
@RequiredArgsConstructor
public class LikeRecordServiceImpl extends ServiceImpl<LikeRecordMapper, LikeRecord> implements ILikeRecordService {

    private final StringRedisTemplate redisTemplate;

    private final ILikedStatService statService;

    /**
     * 点赞或取消点赞
     * @param likeRecordFormDTO
     */
    @Override
    @Transactional
    public void addLikeRecord(LikeRecordFormDTO likeRecordFormDTO, User loginUser) {
        // 1.基于前端的参数，判断是执行点赞还是取消点赞
        boolean success = likeRecordFormDTO.getLiked() ? like(likeRecordFormDTO, loginUser) : unlike(likeRecordFormDTO, loginUser);
        if (!success) {
            return;
        }
        // 3.统计缓存中点赞数量
        Long likedTimes = redisTemplate.opsForSet().size(RedisConstants.LIKE_BIZ_KEY_PREFIX + likeRecordFormDTO.getObjectId());
        if (likedTimes == null) {
            return;
        }
        // 4.缓存点赞总数
        redisTemplate.opsForZSet().add(
                RedisConstants.LIKE_COUNT_KEY_PREFIX + likeRecordFormDTO.getObjType(),
                likeRecordFormDTO.getObjectId().toString(),
                likedTimes);
    }

    /**
     * 查看当前用户是否点赞了
     * @param objectId
     * @return
     */
    @Override
    public boolean isBizLiked(Long objectId, UserVO loginUser) {
        // 1.获取key
        String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + objectId;
        // 2.判断用户是否点赞
        return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(key, loginUser.getId().toString()));
    }

    @Override
    public void readLikedTimes(String objType, int maxObjSize) {
        // 1.批量读取并移除bizId和点赞总数
        String key = RedisConstants.LIKE_COUNT_KEY_PREFIX + objType;
        Set<ZSetOperations.TypedTuple<String>> tuples  = redisTemplate.opsForZSet().popMin(key, maxObjSize);
        if (CollUtil.isEmpty(tuples)) {
            return;
        }
        // 2.数据转换
        List<LikedTimesDTO> list = new ArrayList<>(tuples.size());
        for (ZSetOperations.TypedTuple<String> tuple : tuples) {
            String objectId = tuple.getValue();
            Double likedTimes = tuple.getScore();
            if (objectId == null || likedTimes == null) {
                continue;
            }
            list.add(new LikedTimesDTO(Long.valueOf(objectId), likedTimes.intValue()));
        }

        // 3.批量修改点赞数量
        statService.updateLikedTimes(objType, list);
    }

    private boolean unlike(LikeRecordFormDTO likeRecordFormDTO,  User loginUser) {
        // 1.获取key
        String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + likeRecordFormDTO.getObjectId();
        // 2.删除缓存中的点赞记录
        Long result = redisTemplate.opsForSet().remove(key, loginUser.getId().toString());
        return result != null && result > 0;
    }

    private boolean like(LikeRecordFormDTO likeRecordFormDTO, User loginUser) {
        // 1.获取key
        String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + likeRecordFormDTO.getObjectId();
        // 2.新增点赞记录到缓存
        Long result = redisTemplate.opsForSet().add(key, loginUser.getId().toString());
        return result != null && result > 0;
    }
}
